package com.baomibing.flow.enginee;

import com.alibaba.fastjson.annotation.JSONField;
import com.baomibing.core.exception.ServerRuntimeException;
import com.baomibing.flow.constant.*;
import com.baomibing.flow.exception.FlowExceptionEnum;
import com.baomibing.flow.execute.Execution;
import com.baomibing.flow.model.*;
import com.baomibing.flow.track.ProcessTrack;
import com.baomibing.tool.util.Checker;
import com.google.common.collect.Lists;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * SubProcess
 *
 * @author frog 2023/9/20 15:27
 * @version 1.0.0
 **/
@Getter @ToString
public class SubProcess extends Process{

    @JSONField(deserialize=false)
    private WorkNode source;

    @JSONField(deserialize=false)
    private WorkNode target;
    @JSONField(serialize = false, deserialize = false)
    private TransitionNode action;

    private String actor;

    @Setter
    private String state;

    public boolean doNestProcess() {
        if (this.beComplete()) {
            throw new ServerRuntimeException(FlowExceptionEnum.PROCESS_HAVE_BEEN_COMPLETE);
        }
        doComplete();

        List<TransitionNode> inputs = source.getInputs();
        List<WorkNode> sources = Lists.newArrayList();
        for (TransitionNode tn : inputs) {
            sources.add(tn.getSource());
        }
        ProcessTrack track = new ProcessTrack();
        track.setExecuteTime(new Date()).setAction(action.getName()).setExecuteType(ProcessExecuteEnum.PROCESS)
                .setNodeType(source.getNodeType())
                .setNode(source).setActor(actor).setSources(sources).setTargets(Lists.newArrayList(target));
        flow.updateTrack(track);

        return true;
    }

    public boolean doNestReject() {
        if (this.beNotComplete()) {
            throw new ServerRuntimeException(FlowExceptionEnum.PROCESS_NOT_EXECUTED);
        }
        doNotComplete();

        return true;
    }

    public SubProcess(WorkFlow flow) {
        super(flow);
        this.processType = ProcessTypeEnum.SUB.name();
    }


    private boolean doProcess(String executor) {
        flow.updateWorkNode(source, target);
        List<TransitionNode> inputs = source.getInputs();
        List<WorkNode> sources = Lists.newArrayList();
        for (TransitionNode tn : inputs) {
            sources.add(tn.getSource());
        }
        ProcessTrack track = new ProcessTrack();
        track.setExecuteTime(new Date()).setAction(action.getName()).setExecuteType(ProcessExecuteEnum.PROCESS)
                .setNodeType(source.getNodeType())
                .setNode(source).setActor(executor).setSources(sources).setTargets(Lists.newArrayList(target));
        flow.updateTrack(track);

        doComplete();
        source.setPassed(true);
        action.setPassed(true);
        if (target instanceof EndNode) {
            //执行到结束节点后，将结束节点设置为完成
            target.setPassed( true);
        }
        return true;
    }

    private boolean doReject(String executor) {
        flow.updateWorkNode(target, source);
        List<TransitionNode> inputs = source.getInputs();
        List<WorkNode> sources = Lists.newArrayList();
        for (TransitionNode tn : inputs) {
            sources.add(tn.getSource());
        }
        ProcessTrack track = new ProcessTrack();
        track.setExecuteTime(new Date()).setAction(action.getName()).setExecuteType(ProcessExecuteEnum.REJECT)
                .setNodeType(target.getNodeType())
                .setNode(target).setActor(executor).setSources(sources).setTargets(Lists.newArrayList(source));
        flow.updateTrack(track);
        doNotComplete();
        source.setPassed(false);
        action.setPassed(false);
        if (target instanceof EndNode) {
            target.setPassed(false);
        }
        return true;

    }

    private void doNotComplete() {
        this.state = source.getName();
    }

    private void doComplete() {
        this.state = target.getName();
    }

    public Map<String, Object> getVariable(Execution execution) {
        Map<String, Object> variables = execution.getVariables();
        if (Checker.beEmpty(variables)) {
             throw new ServerRuntimeException(FlowExceptionEnum.VARIABLE_MAP_IS_EMPTY);
        }
        return variables;

    }

    public void doVirtualReject() {
        doNotComplete();
    }

    @Override
    public boolean doProcess(Execution execution) {
        String executor = execution.getActor();
        if (!(target instanceof  ParallelNode)) {
            if (this.beComplete()) {
                throw new ServerRuntimeException(FlowExceptionEnum.PROCESS_HAVE_BEEN_COMPLETE);
            }
        }

        if (source instanceof ForkNode) {
            Map<String, Object> variables = getVariable(execution);
            if (action.getExpression().verify(variables)) {
                return doProcess(executor);
            }
            return false;
        } else if(source instanceof StartNode) {
            doProcess(executor);
            return true;
        } else if (source instanceof ParallelNode) {
            doProcess(executor);
            return true;
        } else if (source instanceof JoinNode) {
            doProcess(executor);
            return true;
        } else if (source instanceof TaskNode) {
            TaskNode taskNode = (TaskNode) source;

            if (Checker.beEmpty(executor)) {
                throw new ServerRuntimeException(FlowExceptionEnum.ACTOR_IS_EMPTY);
            }

            if (taskNode.getActionType() == ActionTypeEnum.ALL || taskNode.getActionType() == ActionTypeEnum.ALL_ORDER) {

                if (taskNode.getBeAllActionComplete()) {
                    return doProcess(executor);
                }

                if (!this.actor.equals(executor)) {
                    throw new ServerRuntimeException(FlowExceptionEnum.ACTOR_NOT_HAVE_PRIVILEGES, this.actor);
                }

                return doNestProcess();
            } else {
                //TODO ROLE POSITION USET FOR ENTRUST
                if (taskNode.getEntrustType() != EntrustTypeEnum.ANY && !taskNode.getEntrusts().contains(executor)) {
                    throw new ServerRuntimeException(FlowExceptionEnum.ACTOR_NOT_HAVE_PRIVILEGES, this.actor);
                }
                this.actor = executor;
            }

            return doProcess(executor);
        }
        throw new ServerRuntimeException(FlowExceptionEnum.NOT_SUPPORT_THE_TYPE_OF_SOURCE_NODE, source.getNodeType());
    }

    @Override
    public boolean doReject(Execution execution) {
        String executor = execution.getActor();
        if (!(target instanceof  ParallelNode)) {
            if (beNotComplete()) {
                throw new ServerRuntimeException(FlowExceptionEnum.PROCESS_NOT_EXECUTED_CAN_NOT_REJECT);
            }
        }
        if (target instanceof ForkNode) {
            doReject(executor);
            return true;
        } else if(target instanceof EndNode) {
            doReject(executor);
            return true;
        } else if (target instanceof ParallelNode) {
            doReject(executor);
            return true;
        } else if (target instanceof JoinNode) {
            doReject(executor);
            return true;
        } else if (target instanceof TaskNode) {
            TaskNode taskNode = (TaskNode) target;
            if (Checker.beEmpty(executor)) {
                throw new ServerRuntimeException(FlowExceptionEnum.ACTOR_IS_EMPTY);
            }


            if (taskNode.getActionType() == ActionTypeEnum.ALL || taskNode.getActionType() == ActionTypeEnum.ALL_ORDER) {
                if (taskNode.getBeAllActionReject()) {
                    return doReject(executor);

                }

                if (!this.actor.equals(executor)) {
                    throw new ServerRuntimeException(FlowExceptionEnum.ACTOR_NOT_HAVE_PRIVILEGES, this.actor);
                }

                return doNestReject();
            } else {
                if (taskNode.getEntrustType() != EntrustTypeEnum.ANY && !taskNode.getEntrusts().contains(executor)) {
                    throw new ServerRuntimeException(FlowExceptionEnum.ACTOR_NOT_HAVE_PRIVILEGES, this.actor);
                }
                this.actor = "";
            }

            return doReject(executor);
        } else {
            throw new ServerRuntimeException(FlowExceptionEnum.NOT_SUPPORT_THE_TYPE_OF_TARGET_NODE, target.getNodeType());
        }

    }

    public boolean beNotComplete() {
        if (Checker.beEmpty(state) || Checker.beNull(source)) {
            return false;
        }
        // 如果驳回策略不是驳回到上一步，则返回false
        boolean beToPreviousRejectPolicy = target instanceof TaskNode && ((TaskNode) target).getRejectPolicy() == RejectPolicyEnum.TO_PREVIOUS;
        if (!beToPreviousRejectPolicy) {
            return false;
        }
        return state.equals(source.getName());
    }

    @Override
    public boolean beComplete() {
        if (Checker.beEmpty(state) || Checker.beNull(target)) {
            return false;
        }
        return state.equals(target.getName());
    }

   public SubProcess source(WorkNode source) {
       this.source = source;
       this.source.setFlow(flow);
       this.state = source.getName();
       return this;
   }

   public SubProcess target(WorkNode target) {
       this.target = target;
       this.target.setFlow(flow);
       return this;
   }

   public SubProcess action(TransitionNode action) {
       this.action = action;
       this.action.setFlow(flow);
       return this;
   }

   public SubProcess actor(String actor) {
        this.actor = actor;
        return this;
   }

}
